﻿#pragma once

#include "../../detail/script_debugger.hh"
#include "../box_std_allocator.hh"
#include "../object_pool.hh"
#include "lua_util.hh"

#include <any>
#include <functional>
#include <sstream>
#include <stack>
#include <thread>
#include <unordered_map>
#include <vector>

namespace kratos {
namespace lua {

class Debugger;
struct Variable;
struct Stack;

class LuaServiceImpl;
// 断点ID
using BreakID = std::string;
// 定义一个单独标识LUA整数的类型
constexpr static int LUA_TINTEGER = LUA_NUMTAGS + 1;
/**
 * Lua变量
 */
struct Variable {
  std::string name;          ///< 变量名
  std::any short_value;      ///< 变量值-短值
  std::any value;            ///< 变量值-长值
  int lua_type_id{LUA_TNIL}; ///< 变量的Lua类型
  /**
   * 变量值字符串
   *
   * \return 变量值字符串
   */
  auto to_short_value_string() const -> std::string {
    switch (lua_type_id) {
    case LUA_TNIL:
      return "nil";
    case LUA_TBOOLEAN:
      return std::to_string(std::any_cast<bool>(short_value));
    case LUA_TINTEGER:
      return std::to_string(std::any_cast<std::int64_t>(short_value));
    case LUA_TNUMBER:
      return std::to_string(std::any_cast<double>(short_value));
    case LUA_TSTRING:
      return std::any_cast<const char *>(short_value);
    case LUA_TTABLE:
    case LUA_TFUNCTION:
    case LUA_TLIGHTUSERDATA:
    case LUA_TUSERDATA:
    case LUA_TTHREAD:
      return std::to_string(std::any_cast<std::ptrdiff_t>(short_value));
    default:
      break;
    }
    return "unknown";
  }

  auto to_value_string() const -> std::string {
    switch (lua_type_id) {
    case LUA_TNIL:
      return "nil";
    case LUA_TBOOLEAN:
      return std::to_string(std::any_cast<bool>(value));
    case LUA_TINTEGER:
      return std::to_string(std::any_cast<std::int64_t>(value));
    case LUA_TNUMBER:
      return std::to_string(std::any_cast<double>(value));
    case LUA_TSTRING:
      return std::any_cast<const char *>(value);
    case LUA_TTABLE:
      return std::any_cast<std::string>(value);
    case LUA_TFUNCTION:
    case LUA_TLIGHTUSERDATA:
    case LUA_TUSERDATA:
    case LUA_TTHREAD:
      return std::to_string(std::any_cast<std::ptrdiff_t>(value));
    default:
      break;
    }
    return "unknown";
  }

  static auto to_value_string(lua_State *L, int index, std::string &value)
      -> bool {
    auto type = lua_type(L, index);
    switch (type) {
    case LUA_TNIL:
      value = "nil";
      break;
    case LUA_TBOOLEAN:
      value = std::to_string(LuaUtil::get<bool>(L, index));
      break;
    case LUA_TINTEGER:
      value = std::to_string(LuaUtil::get<std::int64_t>(L, index));
      break;
    case LUA_TNUMBER:
      value = std::to_string(LuaUtil::get<double>(L, index));
      break;
    case LUA_TSTRING:
      value = LuaUtil::get<const char *>(L, index);
      break;
    case LUA_TFUNCTION:
      value =
          "function@" + std::to_string((std::uint64_t)lua_topointer(L, index));
      break;
    case LUA_TLIGHTUSERDATA:
      value = "lightuserdata@" +
              std::to_string((std::uint64_t)lua_topointer(L, index));
      break;
    case LUA_TUSERDATA:
      value =
          "userdata@" + std::to_string((std::uint64_t)lua_topointer(L, index));
      break;
    case LUA_TTHREAD:
      value =
          "thread@" + std::to_string((std::uint64_t)lua_topointer(L, index));
      break;
      break;
    default:
      value = "Unknown lua type";
      return false;
    }
    return true;
  }

  /**
   * 变量类型字符串
   *
   * \return 变量类型字符串
   */
  auto to_value_type_string() const -> const char * {
    switch (lua_type_id) {
    case LUA_TNIL:
      return "nil";
    case LUA_TBOOLEAN:
      return "bool";
    case LUA_TINTEGER:
      return "integer";
    case LUA_TNUMBER:
      return "number";
    case LUA_TSTRING:
      return "string";
    case LUA_TTABLE:
      return "table";
    case LUA_TFUNCTION:
      return "function";
    case LUA_TLIGHTUSERDATA:
      return "lightuserdata";
    case LUA_TUSERDATA:
      return "userdata";
    case LUA_TTHREAD:
      return "thread";
    default:
      break;
    }
    return "unknown";
  }
};
/**
 * 变量数组
 */
using VariableVector = kratos::service::PoolVector<Variable>;

/**
 * 当前调试栈
 */
struct Stack : public kratos::service::StackBase {
  std::string file_name;          ///< 文件名
  std::string func_name;          ///< 函数名
  std::string func_type;          ///< 函数类型
  std::string func_domain;        ///< 函数作用域
  std::string bp_content;         ///< 断点处文件内容
  int line{0};                    ///< 当前行
  VariableVector local_variables; ///< 局部变量
  VariableVector stack_upvalues;  ///< 闭包的外部变量

  void clear() {
    file_name.clear();
    func_name.clear();
    func_type.clear();
    func_domain.clear();
    bp_content.clear();
    line = 0;
    local_variables.clear();
    stack_upvalues.clear();
  }

  virtual auto empty() const -> bool override { return (line == 0); }

  /**
   * @brief 当前栈调试字符串
   *
   * @return 调试字符串
   */
  virtual auto to_string() const -> std::string override {
    std::stringstream ss;
    ss << std::endl;
    ss << file_name << "(" << func_name << ":" << line << ")(" << func_type
       << ":" << func_domain << ")" << std::endl;
    ss << "Local:" << std::endl;
    for (const auto &var : local_variables) {
      ss << "\t(" << var.to_value_type_string() << ")" << var.name << ":"
         << var.to_short_value_string() << std::endl;
    }
    ss << "Upvalue:" << std::endl;
    for (const auto &var : stack_upvalues) {
      ss << "\t(" << var.to_value_type_string() << ")" << var.name << ":"
         << var.to_short_value_string() << std::endl;
    }
    ss << std::endl << bp_content;
    return ss.str();
  }
  virtual auto upvalues() const -> std::string override {
    std::stringstream ss;
    for (const auto &var : stack_upvalues) {
      ss << "\t(" << var.to_value_type_string() << ")" << var.name << ":"
         << var.to_short_value_string() << std::endl;
    }
    ss << std::endl;
    return ss.str();
  }
  virtual auto locals() const -> std::string override {
    std::stringstream ss;
    for (const auto &var : local_variables) {
      ss << "\t(" << var.to_value_type_string() << ")" << var.name << ":"
         << var.to_short_value_string() << std::endl;
    }
    ss << std::endl;
    return ss.str();
  }
  virtual auto file_path() const -> const std::string & override {
    return file_name;
  }
  virtual auto bp_line() const -> int override { return line; }
};

using StackVector = kratos::service::PoolVector<Stack>;

/**
 * @brief 调试模式
 */
enum class DebugMode : std::uint32_t {
  RUN = 1,       ///< 正常执行
  STEP_IN = 2,   ///< 进入调用过程并断点到过程内第一行
  STEP_OUT = 4,  ///< 跳出调用过程并断点到过程后第一行
  STEP_OVER = 8, ///< 逐行执行不进入函数
};

/**
 * @brief lua调试器
 */
class Debugger : public kratos::service::ScriptDebugger {
  using FuncStack = kratos::service::PoolStack<std::string>;
  using ValueMap = kratos::service::PoolUnorederedMap<std::string, std::string>;
  lua_State *L_{nullptr};                         ///< lua虚拟机
  kratos::service::BreakpointMap breakpoint_map_; ///< 断点表
  Stack current_stack_;                           ///< 当前的栈
  StackVector stack_vector_;                      ///< 断点处调用堆栈
  lua_State *debug_thread_{nullptr};     ///< 当前正在调试的协程
  DebugMode debug_mode_{DebugMode::RUN}; ///< 调试模式
  FuncStack func_name_stack_;            ///< 函数名栈
  std::string current_func_;             ///< 当前正在调用的函数名
  std::string step_out_func_;            ///< 需要跳出的函数名
  std::string step_over_func_;           ///< 需要跳过的函数名
  bool step_in_hit_{false};              ///< 是否需要进行step in
  bool step_out_hit_{false};             ///< 是否需要进行step out
  bool step_over_hit_{false};            ///< 是否需要进行step over
  kratos::service::DebuggerCallback cb_; ///< 调试器事件回调
  std::string source_root_;              ///< 源代码根目录
  bool enable_{false};                   ///< 调试器是否生效
  bool suspend_{false};                  ///< 是否处于挂起状态
  std::string name_;                     ///< 调试器名称
  std::string cur_break_id_;             ///< 当前的断点ID
  int current_frame_{0};                 ///< 当前栈层次

public:
  /**
   * @brief 构造
   *
   * @param L lua虚拟机
   * @param name 调试器名称
   */
  Debugger(lua_State *L, const std::string &name);
  /**
   * 析构s
   */
  virtual ~Debugger();
  /**
   * @brief 主循环
   * @param ms 当前时间戳
   * @return
   */
  virtual auto update(std::time_t ms) -> void override;
  /**
   * @brief 设置源代码根目录
   * @param source_root 源代码根目录
   * @return
   */
  virtual auto set_source_root(const std::string &source_root) -> void override;
  /**
   * @brief 获取源代码根目录
   * @return 源代码根目录
   */
  virtual auto get_source_root() -> const std::string & override;
  /**
   * @brief 设置调试器名称
   * @param name 调试器名称
   * @return
   */
  virtual auto set_name(const std::string &name) -> void override;
  /**
   * @brief 获取调试器名称
   * @return 调试器名称
   */
  virtual auto get_name() const -> const std::string & override;
  /**
   * @brief 禁止调试器
   */
  virtual auto disable() -> void override;
  /**
   * @brief 调试器生效
   */
  virtual auto enable() -> void override;
  /**
   * @brief 调试器是否生效
   * @return true或false
   */
  virtual auto is_enable() -> bool override;
  virtual auto add_breakpoint(const kratos::service::BreakpointMap &breakpoints,
                              bool all_on) -> bool override;
  /**
   * @brief 添加断点
   *
   * @param file 文件名
   * @param line 行号
   * @return 断点ID
   */
  virtual auto add_breakpoint(const std::string &file, int line)
      -> BreakID override;
  /**
   * @brief 删除断点
   * @param break_id 断点ID
   * @return
   */
  virtual auto remove_breakpoint(BreakID break_id) -> void override;
  /**
   * @brief 开启断点
   * @param break_id 断点ID
   * @return
   */
  virtual auto enable_breakpoint(BreakID break_id) -> void override;
  /**
   * @brief 禁用断点
   * @param break_id 断点ID
   * @return
   */
  virtual auto disable_breakpoint(BreakID break_id) -> void override;
  /**
   * @brief 开始执行, 遇到断点挂起
   * @param error 错误描述
   * @return true或false
   */
  virtual auto execute(std::string &error) -> bool override;
  /**
   * @brief step in, 达成条件后挂起
   * @param error 错误描述
   * @return true或false
   */
  virtual auto step_in(std::string &error) -> bool override;
  /**
   * @brief step out, 达成条件后挂起
   * @param error 错误描述
   * @return true或false
   */
  virtual auto step_out(std::string &error) -> bool override;
  /**
   * @brief step over, 达成条件后挂起
   * @param error 错误描述
   * @return true或false
   */
  virtual auto step_over(std::string &error) -> bool override;
  /**
   * @brief 获取所有断点
   * @return 所有断点表
   */
  virtual auto get_all_breakpoint() const
      -> const kratos::service::BreakpointMap & override;
  /**
   * @brief 设置事件回调
   * @param cb 事件回调
   * @return
   */
  virtual auto set_cb(kratos::service::DebuggerCallback cb) -> void override;
  /**
   * @brief 获取事件回调
   * @return 事件回调
   */
  virtual auto get_cb() -> kratos::service::DebuggerCallback override;

  virtual auto is_suspend() -> bool override;

  /**
   * @brief 获取当前调试栈
   * @return 当前调试栈
   */
  virtual auto get_stack() const -> const kratos::service::StackBase & override;

  virtual auto print(const std::string &name, std::string &value)
      -> bool override;
  virtual auto eval(const std::string &code, std::string &result)
      -> bool override;
  virtual auto get_unique_id() const -> std::uint64_t override;
  virtual auto get_backtrace(int level, std::string &backtrace) const
      -> bool override;
  virtual auto frame(int level) -> bool override;
  virtual auto get_machine_version() -> std::string override;

public:
  /**
   * @brief 设置调试钩子
   * @param thread 当前协程
   * @return
   */
  auto hook(lua_State *thread) -> void;
  /**
   * @brief 取消调试钩子
   * @param thread 当前协程
   * @return
   */
  auto unhook(lua_State *thread) -> void;
  /**
   * @brief 获取lua表的调试输出
   * @param l lua协程
   * @param [OUT] str 调试输出
   * @param last_tab 上一级的tab indent
   * @param tab 当前级的tab indent
   * @return true或false
   */
  auto lua_table_to_debug_string(lua_State *l, std::string &str,
                                 const std::string &last_tab,
                                 const std::string &tab) -> bool;
  /**
   * @brief 检查是否具备继续执行的条件
   * @return true或false
   */
  auto check_execution_condition() -> bool;
  /**
   * @brief 获取所有深度的栈信息
   * @return
   */
  auto build_all_stack() -> void;
  /**
   * @brief 获取深度为level的栈信息
   * @param [OUT] stack 栈信息
   * @param level 栈深度
   * @return true或false
   */
  auto get_stack(Stack &stack, int level) -> bool;

private:
  /**
   * @brief 安装钩子
   * @return true或false
   */
  auto install_hook() -> bool;
  /**
   * @brief 卸载钩子
   * @return true或false
   */
  auto uninstall_hook() -> bool;
  /**
   * @brief 获取栈顶的值
   * @param L 协程
   * @param [OUT] value 返回值
   * @param [OUT] type 返回类型
   * @return true或false
   */
  auto get_value(lua_State *L, std::any &value, int &type) -> bool;
  /**
   * @brief 获取栈顶值的短值
   * @param L 协程
   * @param [OUT] value 返回值
   * @param [OUT] type 返回类型
   * @return true或false
   */
  auto get_short_value(lua_State *L, std::any &value, int &type) -> bool;
  /**
   * @brief 行钩子
   * @param L 协程
   * @param ar 调试信息
   * @return
   */
  auto line_hook(lua_State *L, lua_Debug *ar) -> void;
  /**
   * @brief 调用钩子
   * @param L 协程
   * @param ar 调试信息
   * @return
   */
  auto call_hook(lua_State *L, lua_Debug *ar) -> void;
  /**
   * @brief 调用返回钩子
   * @param L 协程
   * @param ar 调试信息
   * @return
   */
  auto ret_hook(lua_State *L, lua_Debug *ar) -> void;
  /**
   * @brief 从断点恢复执行
   * @param error 错误
   * @return true或false
  */
  auto resume(std::string& error) -> bool;

private:
  /**
   * @brief 获取LUA虚拟机全局调试器
   * @param L 协程
   * @return 调试器
   */
  static Debugger *get_lua_debug(lua_State *L);
  /**
   * @brief 钩子入口函数
   * @param L 协程
   * @param ar 调试信息
   * @return
   */
  static auto debug_hook(lua_State *L, lua_Debug *ar) -> void;
};

} // namespace lua
} // namespace kratos
